Completed
Pull Request — development (#2955)
by Stephen
17:46
created

desktop-notify.js ➔ ... ➔ send   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 3
nc 3
nop 1
dl 0
loc 12
rs 9.4285
c 1
b 1
f 0
1
/*! HTML5 Notification - v3.0.0 - 2016-09-19
2
3
Copyright 2016 Tsvetan Tsvetkov
4
5
Licensed under the Apache License, Version 2.0 (the "License");
6
you may not use this file except in compliance with the License.
7
You may obtain a copy of the License at
8
9
     http://www.apache.org/licenses/LICENSE-2.0
10
11
Unless required by applicable law or agreed to in writing, software
12
distributed under the License is distributed on an "AS IS" BASIS,
13
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
See the License for the specific language governing permissions and
15
limitations under the License.
16
*/
17
18
/** @namespace window */
19
/** @namespace window.webkitNotifications */
20
/** @namespace window.external */
21
(function() {
22
    /*
23
     Safari native methods required for Notifications do NOT run in strict mode.
24
     */
25
    //'use strict';
26
    // local variables
27
    var PERMISSION_DEFAULT = "default";
28
    // The user decision is unknown; in this case the application will act as if permission was denied.
29
    var PERMISSION_GRANTED = "granted";
30
    // The user has explicitly granted permission for the current origin to display system notifications.
31
    var PERMISSION_DENIED = "denied";
32
    // The user has explicitly denied permission for the current origin to display system notifications.
33
    var PERMISSION_NOTSUPPORTED = "notsupported";
34
    // The Notification API is not supported on current environment
35
    // map for the old permission values
36
    var PERMISSIONS = [ PERMISSION_GRANTED, PERMISSION_DEFAULT, PERMISSION_DENIED, PERMISSION_NOTSUPPORTED ];
37
    var DIRESCTIONS = [ "auto", "ltr", "rtl" ];
38
    /*
39
        IE does not support Notifications in the same meaning as other modern browsers.
40
        On the other side, IE9+(except MS Edge) implement flashing pinned site taskbar buttons.
41
        Each time new IE Notification is create, previous flashing and icon overlay is cleared.
42
        So, we need to keep track of the notification that calls close method.
43
     */
44
    var IENotificationIndex = -1;
45
    var IECloseNotificationEvents = [ "click", "scroll", "focus" ];
46
    var getIco = function(icon) {
47
        var lastIndex = icon.lastIndexOf(".");
48
        return (lastIndex !== -1 ? icon.substr(0, lastIndex) : icon) + ".ico";
49
    };
50
    /*
51
     * Internal Notificaiton constructor. Keeps the original Notification
52
     * constructor if any or use empty function constructor for browsers
53
     * that do not support Notifications
54
     */
55
    var _Notification = window.Notification || /* Opera Mobile/Android Browser */
56
    window.webkitNotifications && WebKitNotification || /* IE9+ pinned site */
57
    "external" in window && "msIsSiteMode" in window.external && window.external.msIsSiteMode() !== undefined && IENotification || /* Notifications Not supported. Return dummy constructor */
58
    DummyNotification;
59
    /**
60
     * @constructor DummyNotification
61
     */
62
    function DummyNotification() {
63
        var dummyElement = document.createElement("div");
64
        this.addEventListener = function(eventName, callback) {
65
            dummyElement.addEventListener(eventName, callback.bind(this));
66
        };
67
        this.removeEventListener = function(eventName, callback) {
68
            dummyElement.removeEventListener(eventName, callback.bind(this));
69
        };
70
        this.dispatchEvent = function(eventName) {
71
            if (typeof eventName !== "string") {
72
                return;
73
            }
74
            try {
75
                dummyElement.dispatchEvent(new Event(eventName));
76
            } catch (e) {
77
                var event = document.createEvent("Event");
78
                event.initEvent(eventName, false, true);
79
                dummyElement.dispatchEvent(event);
80
            }
81
        };
82
    }
83
    Object.defineProperty(DummyNotification, "permission", {
84
        enumerable: true,
85
        get: function() {
86
            return PERMISSION_NOTSUPPORTED;
87
        }
88
    });
89
    Object.defineProperty(DummyNotification, "requestPermission", {
90
        enumerable: true,
91
        writable: true,
92
        value: function(callback) {
93
            callback(this.permission);
94
        }
95
    });
96
    /**
97
     * @constructor IENotification
98
     */
99
    function IENotification(title, options) {
100
        DummyNotification.call(this);
101
        var notificationIndex = IENotificationIndex;
102
        Object.defineProperties(this, {
103
            close: {
104
                value: function(event) {
105
                    if (notificationIndex === IENotificationIndex) {
106
                        window.external.msSiteModeClearIconOverlay();
107
                        // Remove close events
108
                        IECloseNotificationEvents.forEach(function(event) {
109
                            window.removeEventListener(event, this.close);
110
                        }.bind(this));
111
                        this.dispatchEvent("click");
112
                        this.dispatchEvent("close");
113
                        notificationIndex = null;
114
                    }
115
                }.bind(this)
116
            }
117
        });
118
        // Clear any previous icon overlay
119
        this.close();
120
        // Set icon
121
        if (this.icon) {
122
            window.external.msSiteModeSetIconOverlay(getIco(this.icon), this.description || this.title);
123
        }
124
        // Blink icon
125
        window.external.msSiteModeActivate();
126
        // Trigger show event
127
        this.dispatchEvent("show");
128
        // Attach close event to window
129
        IECloseNotificationEvents.forEach(function(event) {
130
            window.addEventListener(event, this.close);
131
        }.bind(this));
132
        // Increment notification index
133
        notificationIndex = ++IENotificationIndex;
134
    }
135
    Object.defineProperty(IENotification, "permission", {
136
        enumerable: true,
137
        get: function() {
138
            var isTabPinned = window.external.msIsSiteMode();
139
            return isTabPinned ? PERMISSION_GRANTED : PERMISSION_DENIED;
140
        }
141
    });
142
    Object.defineProperty(IENotification, "requestPermission", {
143
        enumerable: true,
144
        writable: true,
145
        value: function(callback) {
146
            return new Promise(function(resolve, reject) {
147
                if (this.permission === PERMISSION_DENIED) {
148
                    alert(this.PERMISSION_REQUEST_MESSAGE);
149
                }
150
                resolve(this.permission);
151
            }.bind(this));
152
        }
153
    });
154
    Object.defineProperty(IENotification, "PERMISSION_REQUEST_MESSAGE", {
155
        writable: true,
156
        value: "IE supports notifications in pinned mode only. Pin this page on your taskbar to receive notifications."
157
    });
158
    /**
159
     * @constructor WebKitNotification
160
     */
161
    function WebKitNotification() {}
162
    Object.defineProperty(WebKitNotification, "permission", {
163
        enumerable: true,
164
        get: function() {
165
            return PERMISSIONS[window.webkitNotifications.checkPermission()];
166
        }
167
    });
168
    Object.defineProperty(WebKitNotification, "requestPermission", {
169
        enumerable: true,
170
        writable: true,
171
        value: function(callback) {
172
            return new Promise(function(resolve, reject) {
173
                window.webkitNotifications.requestPermission(function(permission) {
174
                    resolve(permission);
175
                });
176
            });
177
        }
178
    });
179
    /*
180
        [Safari] Safari6 do not support Notification.permission.
181
        Instead, it support Notification.permissionLevel()
182
     */
183
    if (!_Notification.permission) {
184
        Object.defineProperty(_Notification, "permission", {
185
            enumerable: true,
186
            get: function() {
187
                return _Notification.permissionLevel && _Notification.permissionLevel();
188
            }
189
        });
190
    }
191
    /**
192
     * @constructor Notification
193
     */
194
    function Notification(title, options) {
195
        var dir;
196
        var notification;
197
        if (!arguments.length) {
198
            throw TypeError('Failed to construct "Notification": 1 argument required, but only 0 present.');
199
        }
200
        /*
201
            Chrome display notifications when title is empty screen, but
202
            Safari do NOT.
203
204
            Set title to non-display characted in order to display notifications
205
            in Safari as well when title is empty.
206
         */
207
        if (title === "") {
208
            title = "\b";
209
        }
210
        if (arguments.length > 1 && "object" !== typeof options) {
211
            throw TypeError('Failed to construct "Notification": parameter 2 ("options") is not an object.');
212
        }
213
        dir = Object(options).dir;
214
        if (dir !== undefined && DIRESCTIONS.indexOf(dir) === -1) {
215
            throw TypeError('Failed to construct "Notification": The provided value "' + dir + '" is not a valid enum value of type NotificationDirection.');
216
        }
217
        options = Object(options);
218
        notification = new _Notification(title, options);
219
        /* TODO: actions property */
220
        /* TODO: badge property */
221
        if (!notification.body) {
222
            Object.defineProperty(notification, "body", {
223
                value: String(options.body || "")
224
            });
225
        }
226
        if (!notification.data) {
227
            Object.defineProperty(notification, "data", {
228
                value: options.data || null
229
            });
230
        }
231
        if (!notification.dir) {
232
            Object.defineProperty(notification, "dir", {
233
                value: dir || DIRESCTIONS[0]
234
            });
235
        }
236
        if (!notification.icon) {
237
            Object.defineProperty(notification, "icon", {
238
                value: String(options.icon || "")
239
            });
240
        }
241
        if (!notification.lang) {
242
            Object.defineProperty(notification, "lang", {
243
                value: String(options.lang || "")
244
            });
245
        }
246
        /* TODO: noscreen property */
247
        /* TODO: renotify property */
248
        if (!notification.requireInteraction) {
249
            Object.defineProperty(notification, "requireInteraction", {
250
                value: Boolean(options.requireInteraction)
251
            });
252
        }
253
        /* TODO: sound property */
254
        if (!notification.silent) {
255
            Object.defineProperty(notification, "silent", {
256
                value: Boolean(options.silent)
257
            });
258
        }
259
        if (!notification.tag) {
260
            Object.defineProperty(notification, "tag", {
261
                value: String(options.tag || "")
262
            });
263
        }
264
        if (!notification.title) {
265
            Object.defineProperty(notification, "title", {
266
                value: String(title)
267
            });
268
        }
269
        if (!notification.timestamp) {
270
            Object.defineProperty(notification, "timestamp", {
271
                value: new Date().getTime()
272
            });
273
        }
274
        /* TODO: vibrate property */
275
        return notification;
276
    }
277
    Object.defineProperty(Notification, "permission", {
278
        enumerable: true,
279
        get: function() {
280
            return _Notification.permission;
281
        }
282
    });
283
    /*
284
        Notification.requestPermission should return a Promise(by spec).
285
        Keep the original method and replace it with a custom one that
286
        checks if the call of Notification.requestPermission returns a promise
287
        and if not, then return a custom object that simulates the Promise object.
288
289
        Specification:
290
        Notification.requestPermission().then(callback);
291
292
        Old Spec:
293
        Notification.requestPermission(callback);
294
     */
295
    Object.defineProperty(Notification, "requestPermission", {
296
        enumerable: true,
297
        value: function() {
298
            return new Promise(function(resolve, reject) {
299
                var promise = _Notification.requestPermission(function(permission) {
300
                    resolve(permission);
301
                });
302
                if (!(promise instanceof Promise)) {
303
                    return;
304
                }
305
                resolve(promise);
306
            });
307
        }
308
    });
309
    window.Notification = Notification;
310
})();
311
312
/**
313
 * @name      ElkArte Forum
314
 * @copyright ElkArte Forum contributors
315
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
316
 *
317
 * @version 1.1 Release Candidate 1
318
 *
319
 * This bits acts as middle-man between the notify (above) and the ElkNotifications
320
 * providing the interface required by the latter.
321
 */
322
323
(function() {
324
	var ElkDesktop = (function(opt) {
325
		'use strict';
326
		opt = (opt) ? opt : {};
327
		var notif,
328
			alreadyChecked = false,
329
			alreadyAsked = false,
330
			permission_granted = false;
331
332
		var init = function(opt) {
333
		
334
			notif = Notification;
335
		};
336
337
		var send = function(request) {
338
			if (request.desktop_notifications.new_from_last > 0)
339
			{
340
				if (!hasPermissions())
341
					return;
342
343
				var myNotification = new Notification(request.desktop_notifications.title, {
344
					body: request.desktop_notifications.message,
345
					icon: opt.icon
346
				});
347
			}
348
		};
349
350
		var hasPermissions = function() {
351
			if (alreadyChecked)
352
				return permission_granted;
353
354
			if (alreadyAsked === false && notif.permission == "default" && notif.permission != "notsupported") {
355
				notif.requestPermission();
356
				alreadyAsked = true;
357
			}
358
			permission_granted = notif.permission == "granted"
359
360
			function onPermissionGranted() {
361
				permission_granted = true;
362
			}
363
364
			function onPermissionDenied() {
365
				permission_granted = false;
366
			}
367
368
			alreadyChecked = notif.permission != "default";
369
370
			return permission_granted;
371
		};
372
373
		init(opt);
374
		return {
375
			send: send
376
		};
377
	});
378
379
	// AMD / RequireJS
380
	if ( typeof define !== 'undefined' && define.amd) {
381
		define([], function() {
382
			return ElkDesktop;
383
		});
384
	}
385
	// CommonJS
386
	else if ( typeof module !== 'undefined' && module.exports) {
387
		module.exports = ElkDesktop;
388
	}
389
	// included directly via <script> tag
390
	else {
391
		this.ElkDesktop = ElkDesktop;
392
	}
393
394
})();
395
396